TerraformでAWSのセキュリティグループのルールを作成する方法の比較と注意点
aws providerを使用して、セキュリティグループのルールを作成できるリソースは以下の3つがあります。
- aws_security_group
- aws_security_group_rule
- aws_vpc_security_group_[ingress|egress]_rule
どう違うのか気になったので、ブログにしてみました。
結論
aws_vpc_security_group_[ingress|egress]_rule
を使うのがおすすめ- ルールの定義方法は混在させない
- ルールの競合によって設定が上書きされるリスクがある
ルール単位のタグ付けやimportが可能なため、aws_vpc_security_group_[ingress|egress]_rule
を使うのがおすすめです。
同様のことは、「aws_security_group_rule」でも可能ですが、タグ付けに関してはリソースとルールが1対1でマッピングしていない場合、うまく動作しないことがあります。(後述)
リソース | ルール単位のタグ付け | ルール単位のimport |
---|---|---|
aws_security_group | - | - |
aws_security_group_rule | △ | ◯ |
aws_vpc_security_group_[ingress|egress]_rule | ◯ | ◯ |
- aws_security_group | Resources | hashicorp/aws | Terraform Registry
- TerraformでAWSのセキュリティグループを書く場合に気を付けること その1
- Terraformでセキュリティグループに複数のルールを追加する方法 | 株式会社ビヨンド
比較してみる
それぞれのResourceで以下のセキュリティグループを定義して、比較してみます。
タイプ | プロトコル | ソースCIDR | タグ |
---|---|---|---|
ingress | HTTP | VPC CIDR | |
ingress | HTTPS | VPC CIDR | Name = Hello World (テスト用のタグ) |
egress | all | all |
前提
比較時は、以下のバージョンを利用しています。
- terraform: 1.5.0
- aws-provider: 5.7.0
aws_security_group
aws_security_group | Resources | hashicorp/aws | Terraform Registry
サンプルコード
provider "aws" {} resource "aws_vpc" "this" { cidr_block = "10.0.0.0/16" } resource "aws_security_group" "this" { vpc_id = aws_vpc.this.id name = "example-1" ingress { description = "HTTP from VPC" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [aws_vpc.this.cidr_block] } ingress { description = "HTTPS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.this.cidr_block] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } }
サンプルコードのようにingressを複数書くことで、複数のルールを定義できます。
ルール単位のタグ付け
できません。セキュリティグループ単位でタグを付けることは可能です。
しかし、ingressやegressにはtag定義用の引数が無いためルール単位のタグ付けはできませんでした。
ルール単位のimport
できません。以下のように、セキュリティグループIDを渡してセキュリティグループ単位でimportを行うことは可能です。
$ terraform import aws_security_group.example sg-XXXXX
Terraformで作成したセキュリティグループに手動でルール追加したケースで、ルールをimportしたい場合、上記のようにセキュリティグループごとimportするとエラーになります。
すでに、同一のリソース名がStateファイルに追加されているためです。
一度、セキュリティグループもTerraform管理外から外してimportし直す必要がありそうです。
aws_security_group_rule
aws_security_group | Resources | hashicorp/aws | Terraform Registry
サンプルコード
provider "aws" {} resource "aws_vpc" "this" { cidr_block = "10.0.0.0/16" } resource "aws_security_group" "this" { vpc_id = aws_vpc.this.id name = "example-2" } resource "aws_security_group_rule" "ingress_allow_http" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [aws_vpc.this.cidr_block] security_group_id = aws_security_group.this.id } resource "aws_security_group_rule" "ingress_allow_https" { type = "ingress" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.this.cidr_block] security_group_id = aws_security_group.this.id } resource "aws_security_group_rule" "egress_allow_all" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.this.id } resource "aws_ec2_tag" "test" { resource_id = aws_security_group_rule.ingress_allow_http.security_group_rule_id key = "Name" value = "Hello World" }
ルール単位のタグ付け
できます。リソースの引数にはタグが無いのですが、rule_idを属性として持っています。
そのため、aws_ec2_tag
を利用してルール単位でタグを付けることができます。
注意点として、cidr_blocks
で複数のCIDRを指定するとルールも作成されます。
1つのリソースで複数のルールを作れるため、リソースとルールで1対1のマッピングが成り立たなくなります。
この際、リソースの属性にあるrule_idは空になってしまいます。そのため、タグ付けもうまくできません。
aws_security_group_rule
を使ってルール単位のタグ付けを行う場合は、1リソース1ルールになるように作成する必要があります。
ルール単位のimport
できます。rule_idを使ってimportすることはできないため、ポートやIPアドレスなど指定する必要があります。 (後述の「aws_vpc_security_group_ingress_rule/aws_vpc_security_group_egress_rule」では、rule_idを使ってimportが可能です)
terraform import aws_security_group_rule.egress_allow_all sg-XXXXXX_ingress_-1_0_0_0.0.0.0/0
aws_vpc_security_group_[ingress|egress]_rule
aws_vpc_security_group_ingress_rule | Resources | hashicorp/aws | Terraform Registry aws_vpc_security_group_egress_rule | Resources | hashicorp/aws | Terraform Registry
AWS Provider v4.56.0で追加されたリソースです。
「aws_security_group_rule
があるのに、なぜ追加されたの?」と思う方もいるかもしれません。
正確な経緯は以下を確認するとよさそうです。また、公式ドキュメントにも説明があります。
- Resource Identifiers and Tags for VPC Security Group Rules · Issue #20104 · hashicorp/terraform-provider-aws
- Add `aws_security_group_rules` resource by jakauppila · Pull Request #9032 · hashicorp/terraform-provider-aws
- aws_vpc_security_group_egress_rule | Resources | hashicorp/aws | Terraform Registry
ここでは、ざっくり説明します。
2021年7月にセキュリティグループの各ルールにリソース識別子(ルールID)がつくようになりタグ付けが可能になりました。
aws_security_group_rule
は上記のアップデート前から存在するリソースです。
そのため、ルールIDやタグ付けがうまくできないケースもあります。
例えば、cidr_blocks
で複数のCIDRを指定すれば、その分だけルールが作られます。
結果、リソースとルールが1対1でマッピングしないことがあります。
その状態で、rule_idをoutputすると空になります。rule_idを指定したタグ付けもできません。
resource "aws_security_group_rule" "ingress_allow_http" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" # 複数のルールが作成される cidr_blocks = [aws_vpc.this.cidr_block, "10.0.1.0/24"] security_group_id = aws_security_group.this.id } # ルールIDは空 output "test" { value = aws_security_group_rule.ingress_allow_http.security_group_rule_id }
上記の問題を解消するために、「aws_vpc_security_group_[ingress|egress]_rule」が追加されました。
サンプルコード
provider "aws" {} resource "aws_vpc" "this" { cidr_block = "10.0.0.0/16" } resource "aws_security_group" "this" { vpc_id = aws_vpc.this.id name = "example-3" } resource "aws_vpc_security_group_ingress_rule" "allow_http" { from_port = 80 to_port = 80 ip_protocol = "tcp" cidr_ipv4 = aws_vpc.this.cidr_block security_group_id = aws_security_group.this.id tags = { Name = "Hello World" } } resource "aws_vpc_security_group_ingress_rule" "allow_https" { from_port = 443 to_port = 443 ip_protocol = "tcp" cidr_ipv4 = aws_vpc.this.cidr_block security_group_id = aws_security_group.this.id } resource "aws_vpc_security_group_egress_rule" "allow_all" { ip_protocol = "-1" cidr_ipv4 = "0.0.0.0/0" security_group_id = aws_security_group.this.id }
aws_security_group_rule
と似ていますが、異なる部分もあります。
- ingressとegressでリソースが別れている
- cidr_blockの渡し方が配列ではなく、文字列になっている
- ip_protocolで「-1」を定義した場合、to_portとfrom_portを定義できなくなっている
ルール単位のタグ付け
できます。リソースに引数としてtagsがあります。
ルール単位のimport
できます。以下のようにrule_id単位でimportが可能です。
$ terraform import aws_vpc_security_group_egress_rule.allow_all sgr-XXXXXXX
おわりに
Terraformでセキュリティグループのルール作成する方法の比較でした。
混在させなければ、どれを使っても基本的には問題なく使えると思います。
しかし、Terraformでセキュリティグループ作ってルールだけ手動で作成してしまった。
ルールだけimportしたいケースでは、aws_vpc_security_group_[ingress|egress]_rule
を使用していると楽に対応できます。
既存のコードを直していくのは大変だと思いますが、既存影響なく新規作成する分はaws_vpc_security_group_[ingress|egress]_rule
を使っていきたいと思いました。
以上、AWS事業本部の佐藤(@chari7311)でした。